/*
** Userdata handling.
** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
*/

#define lj_udata_c
#define LUA_CORE

#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_udata.h"
#include "lauxlib.h"
#include "lua.h"

GCudata *lj_udata_new(lua_State *L, MSize sz, GCtab *env)
{
  GCudata *ud = lj_mem_newt(L, sizeof(GCudata) + sz, GCudata);
  global_State *g = G(L);
  newwhite(g, ud);  /* Not finalized. */
  ud->gct = ~LJ_TUDATA;
  ud->udtype = UDTYPE_USERDATA;
  ud->len = sz;
  /* NOBARRIER: The GCudata is new (marked white). */
  setgcrefnull(ud->metatable);
  setgcref(ud->env, obj2gco(env));
  /* Chain to userdata list (after main thread). */
  setgcrefr(ud->nextgc, mainthread(g)->nextgc);
  setgcref(mainthread(g)->nextgc, obj2gco(ud));
  return ud;
}

void LJ_FASTCALL lj_udata_free(global_State *g, GCudata *ud)
{
  lj_mem_free(g, ud, sizeudata(ud));
}

 int bigint_tostring(lua_State *L) {
  BigInt *bi = (BigInt *)luaL_checkudata(L, 1, "LUA_BIGINT");
  char buf[64];
  sprintf(buf, "%ld", bi->value);
  lua_pushstring(L, &buf);
  return 1;
}

int lua_isinteger(lua_State *L, int index) {
    if (lua_isnumber(L, index)) {
        lua_Number n = lua_tonumber(L, index);
        return (n == floor(n));  // 检查是否为整数值
    }
    return 0;
}

 int bigint_add(lua_State *L) {
    BigInt *a = NULL;
    lua_Number num_a = 0;
    int is_a_number = 0;

    if (lua_isuserdata(L, 1) && luaL_checkudata(L, 1, "LUA_BIGINT")) {
        a = (BigInt *)lua_touserdata(L, 1);
    } else if (lua_isnumber(L, 1)) {
        num_a = lua_tonumber(L, 1);
        is_a_number = 1;
    } else {
        return luaL_argerror(L, 1, "expected BigInt or number");
    }

    BigInt *b = NULL;
    lua_Number num_b = 0;
    int is_b_number = 0;

    if (lua_isuserdata(L, 2) && luaL_checkudata(L, 2, "LUA_BIGINT")) {
        b = (BigInt *)lua_touserdata(L, 2);
    } else if (lua_isnumber(L, 2)) {
        num_b = lua_tonumber(L, 2);
        is_b_number = 1;
    } else {
        return luaL_argerror(L, 2, "expected BigInt or number");
    }


    BigInt *res = (BigInt *)lua_newuserdata(L, sizeof(BigInt));
    if (!res) {
        return luaL_error(L, "failed to create BigInt");
    }


    if (!is_a_number && !is_b_number) {
        // BigInt + BigInt
        res->value = a->value + b->value;
    } else if (!is_a_number && is_b_number) {
        // BigInt + number
        res->value = a->value + num_b;
    } else if (is_a_number && !is_b_number) {
        // number + BigInt
        res->value = num_a + b->value;
    } else {
        res->value = num_a + num_b;
    }

    luaL_getmetatable(L, "LUA_BIGINT");
    lua_setmetatable(L, -2);

    return 1;
}


int bigint_sub(lua_State *L) {
    BigInt *a = NULL;
    lua_Number num_a = 0;
    int is_a_number = 0;

    if (lua_isuserdata(L, 1)) {
        a = (BigInt *)luaL_checkudata(L, 1, "LUA_BIGINT");
    } else if (lua_isnumber(L, 1)) {
        num_a = lua_tonumber(L, 1);
        is_a_number = 1;
    } else {
        return luaL_argerror(L, 1, "expected BigInt or number");
    }

    BigInt *b = NULL;
    lua_Number num_b = 0;
    int is_b_number = 0;

    if (lua_isuserdata(L, 2)) {
        b = (BigInt *)luaL_checkudata(L, 2, "LUA_BIGINT");
    } else if (lua_isnumber(L, 2)) {
        num_b = lua_tonumber(L, 2);
        is_b_number = 1;
    } else {
        return luaL_argerror(L, 2, "expected BigInt or number");
    }

    BigInt *res = (BigInt *)lua_newuserdata(L, sizeof(BigInt));
    if (!res) {
        return luaL_error(L, "failed to create BigInt");
    }

    if (!is_a_number && !is_b_number) {
        // BigInt - BigInt
        res->value = a->value - b->value;
    } else if (!is_a_number && is_b_number) {
        // BigInt - number
        res->value = a->value - num_b;
    } else if (is_a_number && !is_b_number) {
        // number - BigInt
        res->value = num_a - b->value;
    } else {
        res->value = num_a - num_b;
    }

    luaL_getmetatable(L, "LUA_BIGINT");
    lua_setmetatable(L, -2);

    return 1;
}

int bigint_mul(lua_State* L) 
{
    BigInt* a = NULL;
    lua_Number num_a = 0;
    int is_a_number = 0;
    
    if (lua_isuserdata(L, 1) && luaL_checkudata(L, 1, "LUA_BIGINT")) {
        a = (BigInt*)lua_touserdata(L, 1);
    } 
    else if (lua_isnumber(L, 1)) {
        num_a = lua_tonumber(L, 1);
        is_a_number = 1;
    }
    else {
        return luaL_argerror(L, 1, "expected BigInt or number");
    }

    BigInt* b = NULL;
    lua_Number num_b = 0;
    int is_b_number = 0;
    
    if (lua_isuserdata(L, 2) && luaL_checkudata(L, 2, "LUA_BIGINT")) {
        b = (BigInt*)lua_touserdata(L, 2);
    }
    else if (lua_isnumber(L, 2)) {
        num_b = lua_tonumber(L, 2);
        is_b_number = 1;
    }
    else {
        return luaL_argerror(L, 2, "expected BigInt or number");
    }

    BigInt* res = (BigInt*)lua_newuserdata(L, sizeof(BigInt));
    if (!res) {
        return luaL_error(L, "memory allocation failed");
    }

    if (!is_a_number && !is_b_number) {
        // BigInt * BigInt
        res->value = a->value * b->value;
    }
    else if (!is_a_number && is_b_number) {
        // BigInt * number
        res->value = a->value * num_b;
    }
    else if (is_a_number && !is_b_number) {
        // number * BigInt
        res->value = num_a * b->value;
    }
    else {
        res->value = num_a * num_b;
    }

    luaL_getmetatable(L, "LUA_BIGINT");
    lua_setmetatable(L, -2);

    return 1;
}

int bigint_div(lua_State* L) 
{
    BigInt* a = NULL;
    lua_Number num_a = 0;
    int is_a_number = 0;
    
    if (lua_isuserdata(L, 1)) {
        a = (BigInt*)luaL_checkudata(L, 1, "LUA_BIGINT");
    } 
    else if (lua_isnumber(L, 1)) {
        num_a = lua_tonumber(L, 1);
        is_a_number = 1;
    }
    else {
        return luaL_argerror(L, 1, "expected BigInt or number");
    }

    BigInt* b = NULL;
    lua_Number num_b = 0;
    int is_b_number = 0;
    
    if (lua_isuserdata(L, 2)) {
        b = (BigInt*)luaL_checkudata(L, 2, "LUA_BIGINT");
    }
    else if (lua_isnumber(L, 2)) {
        num_b = lua_tonumber(L, 2);
        is_b_number = 1;
    }
    else {
        return luaL_argerror(L, 2, "expected BigInt or number");
    }

    // 检查除数是否为0
    if ((!is_b_number && b->value == 0) || (is_b_number && num_b == 0)) {
        return luaL_error(L, "division by zero");
    }

    BigInt* res = (BigInt*)lua_newuserdata(L, sizeof(BigInt));
    if (!res) {
        return luaL_error(L, "memory allocation failed");
    }

    if (!is_a_number && !is_b_number) {
        // BigInt / BigInt
        res->value = a->value / b->value;
    }
    else if (!is_a_number && is_b_number) {
        // BigInt / number
        res->value = a->value / num_b;
    }
    else if (is_a_number && !is_b_number) {
        // number / BigInt
        res->value = num_a / b->value;
    }
    else {
        res->value = num_a / num_b;
    }

    luaL_getmetatable(L, "LUA_BIGINT");
    lua_setmetatable(L, -2);

    return 1;
}

int bigint_mod(lua_State* L) 
{

    BigInt* a = NULL;
    lua_Integer int_a = 0;  // 使用整数类型，因为取模运算需要整数
    int is_a_number = 0;
    
    if (lua_isuserdata(L, 1)) {
        a = (BigInt*)luaL_checkudata(L, 1, "LUA_BIGINT");
    } 
    else if (lua_isinteger(L, 1)) {  // 取模需要整数
        int_a = lua_tointeger(L, 1);
        is_a_number = 1;
    }
    else {
        return luaL_argerror(L, 1, "expected BigInt or integer");
    }

    BigInt* b = NULL;
    lua_Integer int_b = 0;
    int is_b_number = 0;
    
    if (lua_isuserdata(L, 2)) {
        b = (BigInt*)luaL_checkudata(L, 2, "LUA_BIGINT");
    }
    else if (lua_isinteger(L, 2)) {
        int_b = lua_tointeger(L, 2);
        is_b_number = 1;
    }
    else {
        return luaL_argerror(L, 2, "expected BigInt or integer");
    }

    if ((!is_b_number && b->value == 0) || (is_b_number && int_b == 0)) {
        return luaL_error(L, "modulo by zero");
    }

    BigInt* res = (BigInt*)lua_newuserdata(L, sizeof(BigInt));
    if (!res) {
        return luaL_error(L, "memory allocation failed");
    }

    // 执行取模运算
    if (!is_a_number && !is_b_number) {
        // BigInt % BigInt
        res->value = a->value % b->value;
    }
    else if (!is_a_number && is_b_number) {
        // BigInt % number
        res->value = a->value % int_b;
    }
    else if (is_a_number && !is_b_number) {
        // number % BigInt
        res->value = int_a % b->value;
    }
    else {
        // number % number
        res->value = int_a % int_b;
    }

    // 设置元表
    luaL_getmetatable(L, "LUA_BIGINT");
    lua_setmetatable(L, -2);

    return 1;
}

 int bigint_unm(lua_State* L)
{
    BigInt *a = (BigInt *)luaL_checkudata(L, 1, "LUA_BIGINT");

    a->value = -a->value;
    luaL_getmetatable(L, "LUA_BIGINT");
    lua_setmetatable(L, -2);
    return 1;    
}

 int bigint_eq(lua_State* L)
{
    BigInt *a = (BigInt *)luaL_checkudata(L, 1, "LUA_BIGINT");
    BigInt *b = (BigInt *)luaL_checkudata(L, 2, "LUA_BIGINT");
    lua_pushboolean(L, a->value == b->value);    
    return 1;
}

 int bigint_equals(lua_State* L)
{
    BigInt *a = (BigInt *)luaL_checkudata(L, 1, "LUA_BIGINT");
    BigInt *b = (BigInt *)luaL_checkudata(L, 2, "LUA_BIGINT");
    lua_pushboolean(L, a->value == b->value);    
    return 1;
}

 int bigint_len(lua_State* L)
{
    BigInt *a = (BigInt *)luaL_checkudata(L, 1, "LUA_BIGINT");
    tolua_pushint64(L, a->value);
    return 1;
}

int bigint_toint64(lua_State *L) {
    BigInt *a = (BigInt *)luaL_checkudata(L, 1, "LUA_BIGINT");
    
    if (a->value > INT64_MAX || a->value < INT64_MIN) {
        luaL_error(L, "BigInt value out of int64 range");
    }
    
    tolua_pushint64(L, (int64_t)a->value);
    return 1;
}


void lj_udata_init_bigint(lua_State *L) {
if (luaL_newmetatable(L, "LUA_BIGINT")) {
    
    lua_pushcfunction(L, bigint_tostring);
    lua_setfield(L, -2, "__tostring");  
    
    lua_pushcfunction(L, bigint_tostring);
    lua_setfield(L, -2, "tostring");
    
    lua_pushcfunction(L, bigint_add);
    lua_setfield(L, -2, "__add");
    lua_pushcfunction(L, bigint_add);
    lua_setfield(L, -2, "__radd");
    
    lua_pushcfunction(L, bigint_sub);
    lua_setfield(L, -2, "__sub");
    lua_pushcfunction(L, bigint_sub);
    lua_setfield(L, -2, "__rsub");

    lua_pushcfunction(L, bigint_mul);
    lua_setfield(L, -2, "__mul");
    lua_pushcfunction(L, bigint_mul);
    lua_setfield(L, -2, "__rmul");

    lua_pushcfunction(L, bigint_div);
    lua_setfield(L, -2, "__div");
    lua_pushcfunction(L, bigint_div);
    lua_setfield(L, -2, "__rdiv");

    lua_pushcfunction(L, bigint_mod);
    lua_setfield(L, -2, "__mod");
    lua_pushcfunction(L, bigint_mod);
    lua_setfield(L, -2, "__rmod");

    lua_pushcfunction(L, bigint_unm);
    lua_setfield(L, -2, "__unm");

    lua_pushcfunction(L, bigint_eq);
    lua_setfield(L, -2, "__eq");
    
    lua_pushcfunction(L, bigint_equals);
    lua_setfield(L, -2, "equals");   

    lua_pushcfunction(L, bigint_len);
    lua_setfield(L, -2, "__len");   

    lua_pushcfunction(L, bigint_toint64);
    lua_setfield(L, -2, "__toint64"); 
    lua_pushcfunction(L, bigint_toint64);
    lua_setfield(L, -2, "ToInt64");   
}
lua_pop(L, 1); 
}